﻿

using CSRedis;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WHLRDF.Cache
{
    public class RedisService :BaseCacheService, ICacheService, IDisposable
    {
        public int CacheTimeout { get =>60; }

        private CSRedisClient _csredisCache;
        private static RedisService _instance;
        private readonly static object locked = new object();
        public static RedisService Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (locked)
                    {
                        if (_instance == null)
                        {
                            //new MemoryCache(Options.Create(new MemoryCacheOptions()))
                            _instance = new RedisService(ApplicationEnvironments.Site.RedisConnection);
                        }
                    }
                }
                return _instance;
            }
        }
        /// <summary>
        /// 过期时间 秒
        /// </summary>
        public TimeSpan ExpireTime
        {
            get
            {
                return TimeSpan.FromMinutes(CacheTimeout);
            }
        }
        public RedisService()
        {
            _csredisCache = new CSRedisClient(ApplicationEnvironments.Site.RedisConnection);
        }
        public RedisService(string connectionString)
        {
            _csredisCache = new CSRedisClient(connectionString);
        }
        public bool Add(string key, object value)
        {
            IsArgumentNull(key);
            return this.Add(key,value,ExpireTime);
        }
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <param name="expiresSliding">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间,Redis中无效）</param>
        /// <param name="expiressAbsoulte">绝对过期时长(无效)</param>
        /// <returns></returns>
        public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            IsArgumentNull(key);
            return _csredisCache.Set(key, JSONHelper.ToJson(value), expiresSliding.Minutes * CacheTimeout);
        }

        public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return _csredisCache.Set(key, JSONHelper.ToJson(value), expiresIn.Minutes* CacheTimeout);
        }

        public Task<bool> AddAsync(string key, object value)
        {
            IsArgumentNull(key);
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value));
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            IsArgumentNull(key);
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value), expiresSliding.Minutes * CacheTimeout);
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            IsArgumentNull(key);
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value), expiresIn.Minutes * CacheTimeout);
        }

       
        public bool Clear()
        {
            foreach (var key in _csredisCache.Keys("*"))
            {
                _csredisCache.Del(key);
            }
            foreach (var key in _csredisCache.HKeys("*"))
            {
                _csredisCache.HKeys(key);
            }
            return true;
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }

        public bool Exists(string key)
        {
            try
            {
                return _csredisCache.Exists(key);
            }
            catch (Exception)
            {

               
            }
            return true;
        }

        public Task<bool> ExistsAsync(string key)
        {
            IsArgumentNull(key);
            return _csredisCache.ExistsAsync(key);
        }

        public byte[] Get(string key)
        {
            IsArgumentNull(key);
            if (this.Exists(key))
            {
                return _csredisCache.Get<byte[]>(key);
            }
            return null;
        }

        public IDictionary<string, object> GetAll(List<string> keys)
        {
           
            var dict = new Dictionary<string, object>();

            keys.ToList().ForEach(item => dict.Add(item, GetCache(item)));

            return dict;
        }

        public Task<IDictionary<string, object>> GetAllAsync(List<string> keys)
        {
            throw new NotImplementedException();
        }

        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
        {
            IsArgumentNull(key);
            if (this.Exists(key))
            {
                return _csredisCache.GetAsync<byte[]>(key);
            }
            return null;
        }

        public T GetCache<T>(string key) where T : class, new()
        {
            IsArgumentNull(key);
            return _csredisCache.Get<T>(key);
        }

        public object GetCache(string key)
        {
            IsArgumentNull(key);
            var value = _csredisCache.Get(key);
            if (!string.IsNullOrWhiteSpace(value))
            {
                return JsonConvert.DeserializeObject(value);
            }

            return null;
        }

        public Task<T> GetCacheAsync<T>(string key) where T : class, new()
        {
            IsArgumentNull(key);
            return _csredisCache.GetAsync<T>(key);
        }

        public Task<T> GetCacheAsync<T>(string key, Func<T> acquire) where T : class, new()
        {
          
            return Task.Run<T>(() =>
            {
                return this.GetOrCreate<T>(key,acquire);
            });
        }

        public Task<object> GetCacheAsync(string key)
        {
            return Task.Run<object>(() => { return this.GetCache(key); });
        }

        public T GetOrCreate<T>(string key, Func<T> acquire) where T : class, new()
        {
            IsArgumentNull(key);
            if (this.Exists(key))
            {
                var value = _csredisCache.Get(key);
                if (string.IsNullOrWhiteSpace(value))
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            if (result == null)
            {
                return result;
            }
            this.Add(key, result);
            return result;
        }

        public T GetOrCreate<T>(string key, Func<T> acquire, bool isSliding) where T : class, new()
        {
            IsArgumentNull(key);
            if (this.Exists(key))
            {
                var value = _csredisCache.Get(key);
                if (string.IsNullOrWhiteSpace(value))
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            if (result == null)
            {
                return result;
            }
            this.Add(key, result, TimeSpan.FromMinutes(this.CacheTimeout), isSliding);
            return result;
        }

        public void Refresh(string key)
        {
            IsArgumentNull(key);
            if (Exists(key))
            {
                this.Set(key, Get(key), new DistributedCacheEntryOptions()
                {
                    SlidingExpiration = this.ExpireTime
                });
            }
        }

        public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return Task.Run(() =>
            {
                this.Refresh(key);
            });
        }

        public void Remove(string key)
        {
            IsArgumentNull(key);
            _csredisCache.Del(key);
        }

        public void RemoveAll(List<string> keys)
        {
            _csredisCache.Del(keys.ToArray());
        }

        public Task RemoveAllAsync(List<string> keys)
        {

            return  _csredisCache.DelAsync(keys.ToArray());
        }

        public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken))
        {
            IsArgumentNull(key);
            return _csredisCache.DelAsync(key);
        }

        public bool RemoveCache(string key)
        {
            IsArgumentNull(key);
            long count= _csredisCache.Del(key);
            return count > 0;
        }

        public Task<bool> RemoveCacheAsync(string key)
        {
           return Task.Run<bool>(() => {
               return this.RemoveCache(key);
             
            });
           
        }
        
        public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
        {
            IsArgumentNull(key);
            if (value == null)
            {
                return;
            }
 
            _csredisCache.Set(key,value, options.SlidingExpiration.Value.Minutes* this.CacheTimeout);
        }

        public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
        {
            IsArgumentNull(key);
            return _csredisCache.SetAsync(key, value, options.SlidingExpiration.Value.Minutes * this.CacheTimeout);

        }

        #region 
        
        #endregion

        #region hset
        public void HSet<T>(string tableName, string key, T value) where T : class, new()
        {
            if (string.IsNullOrWhiteSpace(tableName) || string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(tableName) + " or " + nameof(key) + " is null");
            }
            _csredisCache.HSet(tableName, key, value);
        }

        public void HSetAsync<T>(string tableName, string key, T value) where T : class, new()
        {
            if (string.IsNullOrWhiteSpace(tableName) || string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(tableName) + " or " + nameof(key) + "");
            }
            if (value == null)
            {
                return;
            }
            _csredisCache.HSetAsync(tableName, key, value);
        }
        public T HGet<T>(string tableName, string key) where T : class, new()
        {
            return _csredisCache.HGet<T>(tableName, key);
        }

        public T HGetOrCreate<T>(string tableName, string key, Func<T> acquire) where T : class, new()
        {
            if (string.IsNullOrWhiteSpace(tableName) || string.IsNullOrWhiteSpace(key))
            {
                return default(T);
            }
            if (_csredisCache.HExists(tableName, key))
            {
                var value = HGet<T>(tableName,key);
                if (value!=null)
                {
                    return value;
                }
            }
            var result = acquire();
            this.HSet<T>(tableName,key, result);
            return result;
        }

        public Task<T> HGetAsync<T>(string tableName, string key) where T : class, new()
        {
            return _csredisCache.HGetAsync<T>(tableName, key);
        }

        public Task<T> HGetOrCreateAsync<T>(string tableName, string key, Func<T> acquire) where T : class, new()
        {
            return Task.Run(() => {
                if (string.IsNullOrWhiteSpace(tableName) || string.IsNullOrWhiteSpace(key))
                {
                    return default(T);
                }
                if (_csredisCache.HExists(tableName, key))
                {
                    var value = HGet<T>(tableName, key);
                    if (value != null)
                    {
                        return value;
                    }
                }
                var result = acquire();
                this.HSet<T>(tableName, key, result);
                return result;
            });
        }

        /// <summary>
        /// 批量移除值
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public bool HRemove<T>(string tableName,params string[] keys)
        {
           var length= _csredisCache.HDel(tableName, keys);
            return length > 0;
        }
        /// <summary>
        /// 批量移除值异步
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public bool HRemoveAsync<T>(string tableName, params string[] keys)
        {
            var length = _csredisCache.HDelAsync(tableName, keys).Result;
            return length > 0;
        }
        #endregion 
    }
}

